/**
 * A query that is sent out by a node to look for a specific event in another 
 * node. If it finds a node that has the event it will give the information
 * about the event to the node that sent the query out. When the query
 * finds the event it removes itself from a list in network so it won't
 * move anymore. 
 * 
 * If the query finds a node that knows at which direction the event is it will
 * move toward that direction and ask the next node where to go. If the node
 * the query moves to is visited by other querys or agents it will stand in
 * a queue until it's the querys turn.  
 * 
 * If the query "dies" before it finds the event, which is after 45 timejumps,
 * it will wait 360 timejumps and then be sent out again to look.
 * @author Markus Sköld (oi11msd)
 */

import java.util.HashMap;
import java.util.Random;

public class Query extends Signal{

	private int wantedEventID;
	public int maxJumps = 45;
	private Node queryNode;
	private Node previousNode;
	private int x, isResurrected;
	
	/**
	 * Creates the query and puts it in a visitqueue at the position 
	 * it is created on. The creator also defines a few arguments.
	 * @param node a Node
	 * @param network a Network
	 */
	public Query(Node node, Network network){
		super(node, network);
		Random random = new Random();
		wantedEventID = random.nextInt(network.EventList.size());

		node.visitorQueue.add(this);
		visited.add(node);
		x = 0;
		isResurrected = 0;
	}
	/**
	 * Returns true if the node that the query is on holds the event
	 * that the query searches for, returns false otherwise. 
	 * @return boolean
	 */
	public boolean NodeHasEvent(){
		if(node.events.containsKey(wantedEventID)){
			return true;
		}
		else{
			return false;
		}
	}

	/**
	 * Returns true if the node that the query is on knows a direction
	 * for where to further look for the event, returns false otherwise.
	 * @return boolean
	 */
	public boolean NodeKnowsEvent(){
		if(node.eventPathHash.containsKey(wantedEventID)){
			return true;
		}
		else{
			return false;
		}
	}

	/**
	 * Moves the query one step. If NodeHasEvent returns true the query
	 * will give the information to the node that sent the query out.
	 * If NodeKnowsEvent returns true the query will move towards the
	 * direction the event is told to be. 
	 * If non of these are true the query will move to a random node that
	 * is neighbor to the one query is on.
	 * 
	 * Non of these moves will be made if the query isnt at the first
	 * position in the queue for the node that it is on. They wont be
	 * made if their timejumps has exceeded their maxjumps neither.
	 */
	public void move(){
		if(x == 1){
			previousNode.visitorQueue.remove();
		}
		if(nrOfJumps < maxJumps){
			if(NodeHasEvent()){

				while(!visited.empty()){
					queryNode = visited.pop();
				}    
				if(node.visitorQueue.peek() == this){
					previousNode = node;
					queryNode.visitorQueue.add(this);
					x++;
				}
				if(queryNode.visitorQueue.peek() == this){
					if(node.visitorQueue.peek() == this) {
						node.visitorQueue.remove();
					}
					//Prints out that a event has been found
					queryNode.events.put(wantedEventID, 
							node.events.get(wantedEventID));
					System.out.println("A event has been found by a query" +
							" at the position (" + node.position.getX() + ":"
							+ node.position.getY() + ") under the " + 
							queryNode.events.get(wantedEventID).spawnTime
							+ " timejump with ID number " + 
							queryNode.events.get(wantedEventID).id);
					queryNode.visitorQueue.remove();
					network.removeQuery(this);
				}
			}
			else if(NodeKnowsEvent()){
				x = 0;
				nrOfJumps--;
				if(node.visitorQueue.peek() == this){
					previousNode = node;

					node = network.network.get(node.eventPathHash.
							get(wantedEventID));
					node.visitorQueue.add(this);
					visited.add(node);
					x = 1;
				}
			}
			else{
				x = 0;
				if(node.visitorQueue.peek() == this){
					previousNode = node;
					Node tempNode;
					HashMap<Integer, Position> neighborsBeen = 
						new HashMap<Integer, Position>();
					Random random = new Random();
					int moveDirection = random.nextInt(node.
							getNeighbours().size());
					tempNode = network.network.get(node.getNeighbours()
							.get(moveDirection));
					neighborsBeen.put(moveDirection, tempNode.position);
					//sees to it that the query doesn't move to a position it
					//has already been.
					while(visited.search(tempNode) != -1){
						moveDirection = random.nextInt(node.getNeighbours()
								.size());
						tempNode = network.network.get(node.getNeighbours()
								.get(moveDirection));
						neighborsBeen.put(moveDirection, tempNode.position);

						if(neighborsBeen.size() == node.getNeighbours()
								.size()){
							break;
						}
					}

					node = network.network.get(node.getNeighbours()
							.get(moveDirection));
					node.visitorQueue.add(this);
					visited.add(node);
					x = 1;
				}
			}
			nrOfJumps++;
		}
		else if(isResurrected == 0){
			node.visitorQueue.remove(this);
			network.removeQuery(this);
			network.addResend(this);
			isResurrected = 1;
			x = 0;
			nrOfJumps = 0;
		}
		else{
			node.visitorQueue.remove(this);
			network.removeQuery(this);
		}
	}

}